﻿using System;
using System.Diagnostics;
using System.IO;
using System.Text;

namespace LogSystem
{
    /// <summary>
    /// 使用说明:
    /// Initilize 初始化
    ///     |
    /// SetLogInfoCallback，SetLogWarningCallback，SetLogErrorCallback or SetAllLogCallback 设置回调
    ///     |
    /// LogInfo 日志输出
    ///     |
    /// Terminate 终止
    /// </summary>
    public class LogSys
    {
        private static MiniThreadPool fileThreadPool;
        private static string saveUrl = "./";
        private static FileStream writeFS;
        private static uint fileMaxSize = 1024 * 1000; //1000kb
        private static string logSuffix = "html";
        /// <summary>
        /// 最终保存路径
        /// </summary>
        private static string finalPath, finalFullPath;
        private static int fileIndex = 0;


        private static Action<object> logInfoCallback;
        private static Action<object> logWarningCallback;
        private static Action<object> logErrorCallback;
        public static void SetLogInfoCallback(Action<object> cb)
        {
            logInfoCallback = cb;
        }
        public static void SetLogWarningCallback(Action<object> cb)
        {
            logWarningCallback = cb;
        }
        public static void SetLogErrorCallback(Action<object> cb)
        {
            logErrorCallback = cb;
        }
        public static void SetAllLogCallback(Action<object> cb)
        {
            logInfoCallback = logWarningCallback = logErrorCallback = cb;
        }

        /// <summary>
        /// 初始化LogSys
        /// </summary>
        /// <param name="saveurl">日志文件保存路径</param>
        public static void Initilize(string saveurl)
        {
            if (fileThreadPool != null) return;
            fileThreadPool = new MiniThreadPool(1);
            saveUrl = saveurl;

            if (!Directory.Exists(saveurl))
                Directory.CreateDirectory(saveUrl);

            if (!saveUrl.EndsWith("/") && !saveUrl.EndsWith("\\"))
                saveUrl += "/";
            finalPath = string.Format("{0}{1}{2}", saveUrl, DateTime.Now.ToString("yyyy-MM-dd HH_mm_ss"), " log");

            CreateFile();
            WriteLogToFile(string.Format("<h2>{0}</h2>", "========== Begin Log ==========\n"));
        }

        private static void CreateFile()
        {
            finalFullPath = string.Format("{0}_{1}.{2}", finalPath, ++fileIndex, logSuffix);

            writeFS = new FileStream(finalFullPath, File.Exists(finalFullPath) ? FileMode.Truncate : FileMode.Create);
        }
        public static void Terminate()
        {
            if (fileThreadPool == null) return;
            WriteLogToFile(string.Format("<h2>{0}</h2>", "========== End Log ==========\n"));
            fileThreadPool.Release();
            writeFS.Close();
            writeFS.Dispose();
            logInfoCallback = null;
            logWarningCallback = null;
            logErrorCallback = null;
        }
        private static byte[] EncodingString(string log)
        {
            return Encoding.UTF8.GetBytes(log);

        }

        private static string AssemblyString(object obj, StackFrame sf, string color, string logType)
        {
            int threadID = System.Threading.Thread.CurrentThread.ManagedThreadId;
            string className = sf.GetMethod().DeclaringType.Name;
            string methodName = sf.GetMethod().Name;
            string time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");

            string logStr = string.Format("<div style=\"background-color: {0};\">[Class:{1}] [Method:{2}] [Thread:{3}]</div>\n<div style=\"background-color: {0};\">{4}</div>\n<div style=\"background-color: {0};\">[{5}] {6}</div><hr/>\n",
                color, className, methodName, threadID, time, logType, obj.ToString());
            return logStr;
        }
        public static void LogInfo(object obj)
        {
            if (logInfoCallback != null)
                logInfoCallback(obj);
            StackFrame sf = GetStackFrame();
            fileThreadPool.PushJob(WriteLogToFile, AssemblyString(obj, sf, "greenyellow", "INFO"));
        }
        public static void LogWarning(object obj)
        {
            if (logWarningCallback != null)
                logWarningCallback(obj);
            StackFrame sf = GetStackFrame();
            fileThreadPool.PushJob(WriteLogToFile, AssemblyString(obj, sf, "yellow", "Warning"));
        }
        public static void LogError(object obj)
        {
            if (logErrorCallback != null)
                logErrorCallback(obj);
            StackFrame sf = GetStackFrame();
            fileThreadPool.PushJob(WriteLogToFile, AssemblyString(obj, sf, "red", "Error"));
        }

        private static void WriteLogToFile(object obj)
        {
            try
            {
                byte[] buffer = EncodingString((string)obj);
                lock (writeFS)
                {
                    writeFS.Write(buffer, 0, buffer.Length);
                    if (writeFS.Length > fileMaxSize)
                    {
                        writeFS.Close();
                        writeFS.Dispose();
                        CreateFile();
                    }
                }
            }
            finally { }

        }

        public static StackFrame GetStackFrame()
        {
            //当前堆栈信息
            StackFrame sf = new StackTrace().GetFrame(2);
            return sf;
        }
        /// <summary>
        /// 调用堆栈
        /// </summary>
        /// <returns></returns>
        public static string GetStackTraceModelName()
        {
            //当前堆栈信息
            System.Diagnostics.StackTrace st = new System.Diagnostics.StackTrace();
            System.Diagnostics.StackFrame[] sfs = st.GetFrames();
            //过虑的方法名称,以下方法将不会出现在返回的方法调用列表中
            string _filterdName = "ResponseWrite,ResponseWriteError,";
            string _fullName = string.Empty, _methodName = string.Empty;
            for (int i = 1; i < sfs.Length; ++i)
            {
                //非用户代码,系统方法及后面的都是系统调用，不获取用户代码调用结束
                if (System.Diagnostics.StackFrame.OFFSET_UNKNOWN == sfs[i].GetILOffset()) break;
                _methodName = sfs[i].GetMethod().Name;//方法名称
                                                      //sfs[i].GetFileLineNumber();//没有PDB文件的情况下将始终返回0

                if (_filterdName.Contains(_methodName)) continue;
                _fullName = _methodName + "()->" + _fullName;
            }
            st = null;
            sfs = null;
            _filterdName = _methodName = null;
            return _fullName.TrimEnd('-', '>');
        }
    }
}
